use anyhow::{anyhow, Result};
use std::fmt::Debug;
use std::ops::{Add, AddAssign, Mul};
use std::sync::mpsc;
use std::thread;
use rand::distributions::{Distribution, Standard};

use rand::Rng;
use crate::{dot_product, Vector};


// 矩阵的结构体
#[derive(Debug)]
pub struct Matrix<T> {
    pub data: Vec<T>,
    pub row: usize,  // 行数
    pub col: usize,  // 列数
}


#[test]
fn test1() {
    let mut rng = rand::thread_rng();
    let num: i32 = rng.gen();

    println!("num is {}", num);
}


// 计算矩阵
pub fn matrix_multiply<T>(m1: &Matrix<T>, m2: &Matrix<T>) -> Result<Matrix<T>>
    where T: Debug + Default + Copy + Add<Output=T> + AddAssign + Mul<Output=T>    //    对泛型 T 的约束 ， T 需要实现了Debug  加法   , 加等于 乘法
{
    if m1.col != m2.row {
        panic!("Matrix size error!");
    }

    let mut data = vec![Default::default(); m1.row * m2.col];
    for i in 0..m1.row {
        for j in 0..m2.col {
            for k in 0..m1.col {
                data[i * m2.col + j] += m1.data[i * m1.col + k] * m2.data[k * m2.col + j];
            }
        }
    }

    let res = Matrix {
        data,
        row: m1.row,
        col: m2.col,
    };


    Ok(res)
}

struct MsgInput<T> {
    index: usize,
    row: Vector<T>,
    col: Vector<T>,
}


impl<T> MsgInput<T> {
    fn new(index: usize, row: Vector<T>, col: Vector<T>) -> MsgInput<T> {
        MsgInput {
            index,
            row,
            col,
        }
    }
}

struct MsgOutput<T> {
    index: usize,
    value: T,
}

impl<T> MsgOutput<T> {
    fn new(index: usize, value: T) -> MsgOutput<T> {
        MsgOutput {
            index,
            value,
        }
    }
}

struct Msg<T> {
    pub input: MsgInput<T>,
    pub sender: oneshot::Sender<MsgOutput<T>>,
}


impl<T> Msg<T> {
    fn new(input: MsgInput<T>, sender: oneshot::Sender<MsgOutput<T>>) -> Msg<T> {
        Msg {
            input,
            sender,
        }
    }
}

// 多线程并发计算矩阵
pub fn matrix_multiply_concurrent<T>(m1: &Matrix<T>, m2: &Matrix<T>) -> Result<Matrix<T>>
    where T: Debug + Send + Default + Copy + Add<Output=T> + AddAssign + Mul<Output=T> + 'static   //    对泛型 T 的约束 ， T 需要实现了Debug  加法   , 加等于 乘法
{
    if m1.col != m2.row {
        panic!("Matrix size error!");
    }

    let senders = (0..NUM_THREADS).map(|_| {
        let (tx, rx)
            = mpsc::channel::<Msg<T>>();

        thread::spawn(move || {
            for msg in rx {
                let value = dot_product(msg.input.row, msg.input.col)?;

                if let Err(e) = msg.sender.send(MsgOutput {
                    index: msg.input.index,
                    value,
                }) {
                    eprintln!("send msg error : {:?}", e);
                }
            }
            Ok::<_, anyhow::Error>(())
        });

        tx
    }).collect::<Vec<_>>();

    let capacity = m1.row * m2.col;
    let mut data = vec![Default::default(); capacity];
    let mut receivers = Vec::with_capacity(capacity);


    for i in 0..m1.row {
        for j in 0..m2.col {
            // 取出每一行的元素
            let row = Vector::new((&m1.data[i * m1.col..(i + 1) * m1.col]).to_vec());

            // 取出每一列的元素
            let col_data = m2.data[j..]
                .iter()
                .step_by(m2.col)
                .copied()
                .collect::<Vec<_>>();
            let col = Vector::new(col_data);
            let index = i * m2.col + j;
            let input = MsgInput::new(index, row, col);
            let (tx, rx) = oneshot::channel();

            let msg = Msg::new(input, tx);
            if let Err(e) = senders[index % NUM_THREADS].send(msg) {
                eprintln!("send error :{:?}", e)
            }
            receivers.push(rx);
        }
    }


    for rx in receivers {
        if let Ok(msg) = rx.recv() {
            data[msg.index] = msg.value;
        } else {
            return Err(anyhow!("receive error"));
        }
    }

    let res = Matrix {
        data,
        row: m1.row,
        col: m2.col,
    };


    Ok(res)
}


const NUM_THREADS: usize = 4;

pub fn fmt<T: std::fmt::Display>(matrix: &Matrix<T>) {
    println!("[");

    for i in 0..matrix.row {
        for j in 0..matrix.col {
            print!("{} ", matrix.data[i * matrix.col + j]);
        }
        println!("")
    }

    println!("]");
}


//    重载乘法运算符
impl<T> Mul<> for Matrix<T>
    where T: Debug + Send + Default + Copy + Add<Output=T> + AddAssign + Mul<Output=T> + 'static
{
    type Output = Matrix<T>;

    fn mul(self, rhs: Self) -> Self::Output {
        matrix_multiply_concurrent(&self, &rhs).expect("mul error ")
    }
}